﻿using Shop.Core.Consts;
using Shop.Core.Repository;
using Shop.Core.Share;
using Shop.IServices;
using Shop.Model.Dto.Goo;
using Shop.Model.Entity.Goo;
using Shop.Model.Entity.System;
using Shop.Model.Entity.Use;
using Shop.Model.View.Goo;
using SqlSugar;

namespace Shop.Services;

/// <summary>
/// 商品
/// </summary>
public class GoodsAppService: AbstractService, IGoodsAppService
{
    private readonly RepositoryBase<GoodsGoods> _goodsRepository;
    private readonly RepositoryBase<GoodsNewProduct> _newProductRepository;
    private readonly RepositoryBase<SysArea> _areaRepository;
    private readonly RepositoryBase<GoodsReview> _reviewRepository;
    private readonly RepositoryBase<GoodsGift> _giftRepository;

    public GoodsAppService(RepositoryBase<GoodsGoods> goodsRepository, 
        RepositoryBase<GoodsNewProduct> newProductRepository,
        RepositoryBase<SysArea> areaRepository,
        RepositoryBase<GoodsReview> reviewRepository,
        RepositoryBase<GoodsGift> giftRepository)
    {
        _goodsRepository = goodsRepository;
        _newProductRepository = newProductRepository;
        _areaRepository = areaRepository;
        _reviewRepository = reviewRepository;
        _giftRepository = giftRepository;
    }

    /// <summary>
    /// 礼包列表
    /// </summary>
    /// <param name="uId">用户Id</param>
    /// <param name="dto">语言(ZH:中文，ULY:维语)</param>
    /// <returns></returns>
    public AppResult<PageModel<GiftBagVo>> GiftBag(long uId, GiftBagDto dto)
    {
        var isZh = dto.Language.Equals(LanguageConsts.ZH);
        var records = 0;
        var useGrade = _goodsRepository.Queryable<UseUser>().Where(x => x.Id == uId).Select(x => x.Grade).First();
        var data = _goodsRepository.Queryable<GoodsGoods, GoodsSize, GoodsPrice, GoodsUserLevel>(
                (t1, t2, t3, t4) => new object[]
                {
                    JoinType.Left, t1.Id == t2.GoodsId && t2.IsDefault == true,
                    JoinType.Left, t2.Id == t3.Size && t1.Id == t3.Goods,
                    JoinType.Left, t1.Id == t4.GoodsId
                })
            .Where((t1, t2, t3, t4) => t1.Type == 2 && t1.Saleable ==true && t1.Valid == true)
            .Where((t1, t2, t3, t4) => (t1.UpTime == null || t1.UpTime <= DateTime.Now) &&
                                       (t1.DownTime == null || t1.DownTime > DateTime.Now))
            .OrderBy((t1, t2, t3, t4) => t1.Id)
            .Select((t1, t2, t3, t4) => new GiftBagVo()
            {
                Id = t1.Id,
                Img = isZh?t1.Cover:t1.UighurCover,
                LineationPrice = t1.LineationPrice,
                Money = useGrade == 0 ? t3.Price :
                    useGrade == 1 ? t3.OnePrice :
                    useGrade == 2 ? t3.TwoPrice : t3.ThreePrice,
                Name = isZh?t1.Name:t1.UighurName,
                IsPostage = t1.IsPostage,
                Count = t2.Count,
                GiftBagGrade = t4.UserGrade
            }).ToPageList(dto.PageIndex, dto.PageSize, ref records);
        return new PageModel<GiftBagVo>(dto,data,records);
    }

    /// <summary>
    /// 首页新品
    /// </summary>
    /// <param name="uId"></param>
    /// <param name="language"></param>
    /// <returns></returns>
    /// <exception cref="NewGoodsVo"></exception>
    public AppResult<NewGoodsVo> NewGoo(long uId,string language)
    {
        var isZh = language.Equals(LanguageConsts.ZH);
        var useGrade = _goodsRepository.Queryable<UseUser>().Where(x => x.Id == uId).Select(x => x.Grade).First();
        var data = _newProductRepository.Queryable<GoodsNewProduct, GoodsGoods, GoodsSize, GoodsPrice>(
                (t1, t2, t3, t4) => new object[]
                {
                    JoinType.Left, t1.Goods == t2.Id,
                    JoinType.Left, t1.Goods == t3.GoodsId && t3.IsDefault == true,
                    JoinType.Left, t1.Goods == t4.Goods && t3.Id == t4.Size
                })
            .Where((t1, t2, t3, t4) => t1.IsDelete == false)
            .Select((t1, t2, t3, t4) => new NewGoodsVo()
            {
                Id = t1.Id,
                Goods = t1.Goods,
                Name = isZh ? t2.Name : t2.UighurName,
                Content = t1.Content,
                Spe = t1.Spe,
                Unit = t1.Unit,
                Img = isZh ? t1.Img : t1.UighurImg,
                Title = isZh ? t1.Title : t1.UighurTitle,
                LineationPrice = t2.LineationPrice,
                Money = useGrade == 0 ? t4.Price :
                    useGrade == 1 ? t4.OnePrice :
                    useGrade == 2 ? t4.TwoPrice : t4.ThreePrice,
            }).First();
        return data;
    }

    /// <summary>
    /// 商品列表
    /// </summary>
    /// <param name="uId"></param>
    /// <param name="dto"></param>
    /// <returns></returns>
    /// <exception cref="GooListVo"></exception>
    public async Task<PageModel<GooListVo>> GooList(long uId, GooListDto dto)
    {
        var isZh = dto.Language.Equals(LanguageConsts.ZH);
        RefAsync<int> records = 0;
        var useGrade = await _goodsRepository.Queryable<UseUser>().Where(x => x.Id == uId).Select(x => x.Grade).FirstAsync();
        var data = await _goodsRepository.Queryable<GoodsGoods, GoodsSize, GoodsPrice, GoodsApplyScope>(
                (t1, t2, t3, t4) => new object[]
                {
                    JoinType.Left, t1.Id == t2.GoodsId && t2.IsDefault == true,
                    JoinType.Left, t2.Id == t3.Size && t1.Id == t3.Goods,
                    JoinType.Left, t1.Id == t4.GoodsId,
                })
            .WhereIF(!string.IsNullOrEmpty(dto.Search), (t1, t2, t3, t4) => t1.Name.Contains(dto.Search))
            .Where((t1, t2, t3, t4) => t1.Valid == true && t1.Saleable == true && t1.Type == 1)
            .Where((t1, t2, t3, t4) => (t1.UpTime == null || t1.UpTime <= DateTime.Now) &&
                                           (t1.DownTime == null || t1.DownTime > DateTime.Now))
            .OrderBy("t1.Type Desc, t1.FoundTime Desc, t1.Id")
            .Select((t1, t2, t3, t4) => new GooListVo()
            {
                Id = t1.Id,
                Sign = t4.Sign,
                UserGroups = isZh ? t4.UserGroups : t4.UighurUserGroups,
                Symptom = isZh ? t4.Symptom : t4.UighurSymptom,
                Cover = isZh ? t1.Cover : t1.UighurCover,
                LineationPrice = t1.LineationPrice,
                Money = useGrade == 0 ? t3.Price :
                    useGrade == 1 ? t3.OnePrice :
                    useGrade == 2 ? t3.TwoPrice : t3.ThreePrice,
                Count = t2.Count,
                IsPostage = t1.IsPostage,
                Name = isZh ? t1.Name : t1.UighurName
            }).ToPageListAsync(dto.PageIndex, dto.PageSize, records);
        return new PageModel<GooListVo>(dto,data,records);
    }

    /// <summary>
    /// 商品详情
    /// </summary>
    /// <param name="uId"></param>
    /// <param name="dto"></param>
    /// <returns></returns>
    /// <exception cref="GooParticularsVo"></exception>
    public AppResult<GooParticularsVo> Particulars(long uId, ParticularsDto dto)
    {
        var isZh = dto.Language.Equals(LanguageConsts.ZH);
        var useGrade = _goodsRepository.Queryable<UseUser>().Where(x => x.Id == uId).Select(x => x.Grade).First();
        var data = _goodsRepository.Includes(x => x.Size, y => y.Price)
            .Where(x => x.Id == dto.GId)
            .ToList(x => new GooParticularsVo()
            {
                Id = x.Id,
                Name = isZh ? x.Name : x.UighurName,
                Tag = isZh ? x.Tag : x.UighurTag,
                Banner = isZh ? x.Banner : x.UighurBanner,
                Details = isZh ? x.Details : x.UighurDetails,
                LineationPrice = x.LineationPrice,
                Money = x.Size.Select(z =>
                    useGrade == 0 ? z.Price.Price :
                    useGrade == 1 ? z.Price.OnePrice :
                    useGrade == 2 ? z.Price.TwoPrice : z.Price.ThreePrice).First(),
                BelowMoney = x.Size.Select(z =>
                    useGrade == 0 ? z.Price.OnePrice :
                    useGrade == 1 ? z.Price.TwoPrice :
                    useGrade == 2 ? z.Price.ThreePrice : 0).First(),
                Spc = x.Spc,
                Size = x.Size.Select(y => new GooSizeParticularsVo()
                {
                    SId = y.Id,
                    SName = isZh ? y.Name : y.UighurName,
                    SImg = y.Thumbnail,
                    Stock = y.Stock,
                    Count = y.Point,
                    SMoney = useGrade == 0 ? y.Price.Price :
                        useGrade == 1 ? y.Price.OnePrice :
                        useGrade == 2 ? y.Price.TwoPrice : y.Price.ThreePrice,
                    ShareSubsidy = useGrade == 0 ? 0 :
                        useGrade == 1 ? y.Price.Price - y.Price.OnePrice :
                        useGrade == 2 ? y.Price.Price - y.Price.TwoPrice : y.Price.Price - y.Price.ThreePrice,
                    IsDefault = y.IsDefault
                }).ToList()
            })
            .First();
        return data;
    }

    /// <summary>
    /// 商品运费
    /// </summary>
    /// <param name="dto"></param>
    /// <returns></returns>
    /// <exception cref="decimal"></exception>
    public async Task<AppResult<CalculateDeliveryCostVo>> CalculateDeliveryCost(CalculateDeliveryCostDto dto)
    {
        // 初始化运费金额
        var calculateDeliveryCostVo = new CalculateDeliveryCostVo()
        {
            Goods = new List<DeliveryGoodsVo>()
        };
        // 判断地址信息
        var provinceInfo = await _areaRepository.Where(x => x.Code == dto.ProvinceCode && x.Level == 1).FirstAsync();
        if (provinceInfo == null)
            return new AppResult<CalculateDeliveryCostVo>() { Code = 400, Message = "失败", Errors = new string[] { "省份不存在" } };
        else
        {
            var cityInfo = await _areaRepository.Where(x => x.Code == dto.CityCode && x.Parent_Code == dto.ProvinceCode && x.Level == 2).FirstAsync();
            if (cityInfo == null)
                return new AppResult<CalculateDeliveryCostVo>() { Code = 400, Message = "失败", Errors = new string[] { "地区错误" } };
            else
            {
                var districtInfo = await _areaRepository.Where(x => x.Code == dto.DistrictCode && x.Parent_Code == dto.CityCode && x.Level == 3).FirstAsync();
                if (districtInfo == null)
                    return new AppResult<CalculateDeliveryCostVo>() { Code = 400, Message = "失败", Errors = new string[] { "地区错误" } };
            }
        }
        var goodsIds = dto.Goods.Select(x => x.GoodsId).ToList();
        // 配送费用详情
        var priceList = await _goodsRepository.Queryable<GoodsGoods, GoodsDeliveryTemp, GoodsDeliveryCity, GoodsDeliveryPrice>(
                (t1, t2, t3, t4) => new object[]
                {
                    JoinType.Left, t1.DeliveryTemp == t2.Id && t2.IsDelete == false && t2.IsEnabled == true,
                    JoinType.Inner, t2.Id == t3.DeliveryTemp,
                    JoinType.Left, t3.DeliveryPrice == t4.Id
                })
            .Where((t1, t2, t3, t4) => goodsIds.Contains(t1.Id))
            .Where((t1, t2, t3, t4) => 
                (t3.ProvincelId == dto.ProvinceCode && t3.CitiesId == dto.CityCode && t3.AreasId == dto.DistrictCode)
                ||
                (t3.ProvincelId == dto.ProvinceCode && t3.CitiesId == dto.CityCode && t3.AreasId == null)
                || 
                (t3.ProvincelId == dto.ProvinceCode && t3.CitiesId == null && t3.AreasId == null) 
                ||
                t4.IsDefault == true)
            .Select((t1, t2, t3, t4) => new priceListVo()
            {
                GId = t1.Id,
                Level = t3.Level,
                Id = t4.Id,
                IsDefault = t4.IsDefault,
                FirstPiece = t4.FirstPiece,
                ContinuationPiece = t4.ContinuationPiece,
                FirstCost = t4.FirstCost,
                ContinuationCost = t4.ContinuationCost,
                IsFreeFullMoney = t4.IsFreeFullMoney,
                FreeShippingCost = t4.FreeShippingCost,
                IsFreeFullPiece = t4.IsFreeFullPiece,
                FreeShippingCount = t4.FreeShippingCount
            })
            .ToListAsync();
        // 配送模板为null时包邮
        if (priceList.Count == 0) calculateDeliveryCostVo.SumFreight = 0;
        else
        {
            foreach (var item in dto.Goods)
            {
                var deliveryGoodsVo = new DeliveryGoodsVo()
                {
                    Gid = item.GoodsId
                };
                var priceInfo = priceList.Where(x => x.GId == item.GoodsId).OrderBy(x => x.IsDefault).ThenBy(x => x.Level).First();
                // (是否满额包邮 && 商品合计金额 “大于等于“ 包邮费用) || (是否满件包邮 && 商品数量 “大于等于“ 包邮件数) 则为包邮
                if ((priceInfo.IsFreeFullMoney && item.Total >= priceInfo.FreeShippingCost) ||
                    (priceInfo.IsFreeFullPiece && item.Count >= priceInfo.FreeShippingCount))
                    deliveryGoodsVo.Freight = 0;
                else
                {
                    // (订单商品数 ”大于“ 首件件数 && 续件件数 ”大于“ 0) 计算附加配送费
                    if (item.Count > priceInfo.FirstPiece && priceInfo.ContinuationPiece > 0)
                    {
                        // 续件总数 =  订单商品数-首件件数
                        var continuationsNumber = item.Count - priceInfo.FirstPiece;
                        // 续件件数=续件总数/续件件数
                        // Math.Ceiling：向上进位取整。例：Math.Ceiling(2.1)=3
                        var continuationPiece = Math.Ceiling(((decimal)continuationsNumber / priceInfo.ContinuationPiece));
                        //配送费用 = 首件费用 + 续件件数 * 续件费用
                        deliveryGoodsVo.Freight = priceInfo.FirstCost + continuationPiece * priceInfo.ContinuationCost;
                    }
                    else
                        deliveryGoodsVo.Freight = priceInfo.FirstCost;
                }
                calculateDeliveryCostVo.SumFreight += deliveryGoodsVo.Freight;
                calculateDeliveryCostVo.Goods.Add(deliveryGoodsVo);
            }
        }
        return calculateDeliveryCostVo;
    }

    /// <summary>
    /// 商品评论
    /// </summary>
    /// <param name="dto"></param>
    /// <returns></returns>
    /// <exception cref="ReviewVo"></exception>
    public PageModel<ReviewVo> Review(ReviewDto dto)
    {
        var records = 0;
        var data = _reviewRepository.Queryable<GoodsReview, UseUser>((t1, t2) => new object[]
            {
                JoinType.Left, t1.UserId == t2.Id
            }).Where((t1, t2) => t1.GoodsId == dto.GId)
            .OrderBy("t1.CreateTime Desc, t1.Id")
            .Select((t1, t2) => new ReviewVo()
            {
                Head = t2.Head,
                Name = t2.Nickname,
                Content = t1.Content,
                Video = t1.Video,
                Image = t1.Image,
                Score = t1.Score,
                CreateTime = t1.CreateTime
            })
            .ToPageList(dto.PageIndex, dto.PageSize, ref records);
        return new PageModel<ReviewVo>(dto,data,records);
    }

    /// <summary>
    /// 商品赠送规则
    /// </summary>
    /// <param name="dto"></param>
    /// <returns></returns>
    /// <exception cref="NotImplementedException"></exception>
    public AppResult<GiftRulesVo> GiftRules(GiftRulesDto dto)
    {
        var goodsInfo = _goodsRepository.Where(x => x.Id == dto.GId && x.Saleable == true && x.Valid == true).First();
        if (goodsInfo == null)
            return Problem("商品信息错误");
        var data = _giftRepository.Queryable<GoodsGift, GoodsSize, GoodsGoods>((t1, t2, t3) => new object[]
            {
                JoinType.Left, t1.GiftSpecId == t2.Id,
                JoinType.Left, t2.GoodsId == t3.Id,
            })
            .Where((t1, t2, t3) => t1.GoodsId == dto.GId && t1.GoodsSpecId == dto.SId && t1.IsDel == false)
            .Select((t1, t2, t3) => new GiftRulesVo()
            {
                GName = t3.Name,
                SName = t2.Name,
                BuyCount = t1.BuyCount,
                Count = t1.GiveCount,
                IsSuperposition = t1.IsSuperposition,
                IsLimited = t1.IsLimited,
                RemainderCount = t1.RemainderCount
            })
            .First();
        return data;
    }
}